package com.lute.model;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.springframework.security.oauth2.common.json.JSONException;
import org.springframework.security.oauth2.common.json.JSONObject;

import com.lute.json.AccountingPeriodVerificationJSON;
import com.lute.json.SummaryStatusJSON;
import com.lute.utils.DAOFactory;
import com.lute.utils.ServerErrorResponse;

@Entity
public class EmployeeAccountingPeriod {

	private int id_employee_accounting_period;
	/* WARN: from must be the same as clientAccountingPeriod */
	private GregorianCalendar calFrom; 
	private Employee employee;
	private Approver approver;
	private Integer calendarApprover;
	private String employeeAccountingPeriodState;
	private ClientAccountingPeriod clientAccountingPeriod;
	private String rejectedBy;
	private String rejectedByUserRole;
	private String rejectionReason;
	private GregorianCalendar rejectionDate;
	private List<NotepadEntry> notepadEntries   =	new ArrayList<NotepadEntry>();
	private List<EmployeeEntry> employeeEntries =  new ArrayList<EmployeeEntry>();

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	public int getId_employee_accounting_period() {
		return id_employee_accounting_period;
	}
	public void setId_employee_accounting_period(int id_employee_accounting_period) {
		this.id_employee_accounting_period = id_employee_accounting_period;
	}
	
	public GregorianCalendar getCalFrom() {
		return calFrom;
	}
	public void setCalFrom(GregorianCalendar calFrom) {
		this.calFrom = calFrom;
	}
	
	@ManyToOne
	@JoinColumn(name="id_employee")
	public Employee getEmployee() {
		return employee;
	}
	public void setEmployee(Employee employee) {
		this.employee = employee;
	}
	
	@ManyToOne
	@JoinColumn(name="id_approver")
	public Approver getApprover() {
		return approver;
	}
	public void setApprover(Approver approver) {
		this.approver = approver;
	}
	
	public Integer getCalendarApprover() {
		return calendarApprover;
	}
	public void setCalendarApprover(Integer calendarApprover) {
		this.calendarApprover = calendarApprover;
	}
	
	public String getEmployeeAccountingPeriodState() {
		return employeeAccountingPeriodState;
	}
	public void setEmployeeAccountingPeriodState(
			String employeeAccountingPeriodState) {
		this.employeeAccountingPeriodState = employeeAccountingPeriodState;
	}

	public String getRejectedBy() {
		return rejectedBy;
	}
	public void setRejectedBy(String rejectedBy) {
		this.rejectedBy = rejectedBy;
	}
	public String getRejectedByUserRole() {
		return rejectedByUserRole;
	}
	public void setRejectedByUserRole(String rejectedByUserRole) {
		this.rejectedByUserRole = rejectedByUserRole;
	}
	
	public String getRejectionReason() {
		return rejectionReason;
	}
	public void setRejectionReason(String rejectionReason) {
		this.rejectionReason = rejectionReason;
	}
	
	public GregorianCalendar getRejectionDate() {
		return rejectionDate;
	}
	public void setRejectionDate(GregorianCalendar rejectionDate) {
		this.rejectionDate = rejectionDate;
	}
	
	@ManyToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="id_client_accounting_period")
	public ClientAccountingPeriod getClientAccountingPeriod() {
		return clientAccountingPeriod;
	}
	public void setClientAccountingPeriod(
			ClientAccountingPeriod clientAccountingPeriod) {
		this.clientAccountingPeriod = clientAccountingPeriod;
	}
	@OneToMany(targetEntity=NotepadEntry.class, cascade=CascadeType.ALL, fetch=FetchType.LAZY)
	@JoinColumn(name="employeeAccountingPeriod")
	public List<NotepadEntry> getNotepadEntries() {
		return notepadEntries;
	}
	public void setNotepadEntries(List<NotepadEntry> notepadEntries) {
		this.notepadEntries = notepadEntries;
	}
	@OneToMany(targetEntity=EmployeeEntry.class, cascade=CascadeType.ALL, fetch=FetchType.LAZY)
	@JoinColumn(name="employeeAccountingPeriod")
	public List<EmployeeEntry> getEmployeeEntries() {
		return employeeEntries;
	}
	public void setEmployeeEntries(List<EmployeeEntry> employeeEntries) {
		this.employeeEntries = employeeEntries;
	}
	
	public static JSONObject doAccountingPeriodVerification(EmployeeAccountingPeriod period, JSONObject jsonRes) throws JSONException {
		List<EmployeeEntry> employeeEntries = period.getEmployeeEntries();
		Integer creativityPercentage = null;
		
		if(period.getEmployee() == null) {
			if(!(period.getApprover().getApproverProfile().getPositions().getName().equals(PositionName.SUPPORT.toString()))) {
				creativityPercentage = period.getApprover().getApproverProfile().getPositions().getCreativity_percentage();
			}
		} else {
			if(!(period.getEmployee().getEmployeeProfil().getPositions().getName().equals(PositionName.SUPPORT.toString()))) {
				creativityPercentage = period.getEmployee().getEmployeeProfil().getPositions().getCreativity_percentage();
			}
		}
		int workHours = 0;
		int creativeHoursSum = 0;
		
		if(employeeEntries.size() == 0) {
			jsonRes.put("result",ServerErrorResponse.CALENDAR_IS_EMPTY.toString());
			return jsonRes;
		}

		for(EmployeeEntry entry: employeeEntries) {
			
			if(entry.getHours() < 8.0) {
				jsonRes.put("result",ServerErrorResponse.CALENDAR_CONTAINS_DAYS_BELOW_8H.toString());
				return jsonRes;
			}
			
			if(entry.getCompanyCategory() == null) {
				jsonRes.put("result",ServerErrorResponse.CALENDAR_CONTAINS_EMPTY_DAYS.toString());
				return jsonRes;
			}
			
			if(entry.getCompanyCategory().getName().equals(CompanyHoursCategory.WORK_CLIENT.toString()) ||
			   entry.getCompanyCategory().getName().equals(CompanyHoursCategory.WORK_COMPANY.toString()))
			{
				workHours += entry.getHours();
			}
			creativeHoursSum += entry.getCreativeHours();
		}
		if(creativityPercentage != null) {
			float creativeHoursBase = ((float)(workHours * creativityPercentage)) / 100;
			int creativeHours = 0;
			
			if( ((int)(creativeHoursBase + 0.5)) > (int)creativeHoursBase ) {
				creativeHours = ((int)creativeHoursBase) + 1;
			} else {
				creativeHours = (int)creativeHoursBase;
			}
					
			if( (creativeHoursSum > creativeHours)) {
				jsonRes.put("result",ServerErrorResponse.TOO_MANY_CREATIVE_HOURS.toString());
				return jsonRes;
			}
			if((creativeHoursSum < creativeHours )) {
				jsonRes.put("result",ServerErrorResponse.NOT_ENOUGH_CREATIVE_HOURS.toString());
				return jsonRes;
			}
		}
		
		return jsonRes;
	}
	
	public static SummaryStatusJSON prepareSummaryStatus(List<EmployeeAccountingPeriod> periods, SummaryStatusJSON status, String userRole) 
												  throws JSONException {
		int checkedByApproverCount = 0;
		int checkedByClientAdminCount = 0;
		int initialCount = 0;
		int rejectedByApproverCount = 0;
		int rejectedByClientAdminCount = 0;
		int sentToApproverCount = 0;
		int sentToClientAdminCount = 0;
		int notConfigured = 0;
		int workingForClient = 0;
		int benched = 0;
		
		for(EmployeeAccountingPeriod period: periods) {
			if(period.getEmployeeAccountingPeriodState().equals(EmployeeAccountingPeriodState.CHECKED_BY_APPROVER.toString())) {
				checkedByApproverCount++;
			} else if(period.getEmployeeAccountingPeriodState().equals(EmployeeAccountingPeriodState.CHECKED_BY_CLIENT_ADMIN.toString())) {
				checkedByClientAdminCount++;
			} else if(period.getEmployeeAccountingPeriodState().equals(EmployeeAccountingPeriodState.INITIAL.toString())) {
				initialCount++;
			} else if(period.getEmployeeAccountingPeriodState().equals(EmployeeAccountingPeriodState.REJECTED_BY_APPROVER.toString())) {
				rejectedByApproverCount++;
			} else if(period.getEmployeeAccountingPeriodState().equals(EmployeeAccountingPeriodState.REJECTED_BY_CLIENT_ADMIN.toString())) {
				rejectedByClientAdminCount++;
			} else if(period.getEmployeeAccountingPeriodState().equals(EmployeeAccountingPeriodState.SENT_TO_APPROVER.toString())) {
				sentToApproverCount++;
			} else if(period.getEmployeeAccountingPeriodState().equals(EmployeeAccountingPeriodState.SENT_TO_CLIENT_ADMIN.toString())) {
				sentToClientAdminCount++;
			} else {
				/* do nothing */
			}
			
			if(userRole.equals("clientAdmin")) {
				/* approver */
				if(period.getEmployee() == null) {
					if(period.getApprover().getApproverProfile().getEmployeeCurrentState().equals(EmployeeCurrentState.WORKING_FOR_CLIENT.toString())) {
						workingForClient++;
					}
					if(period.getApprover().getApproverProfile().getEmployeeCurrentState().equals(EmployeeAccountingPeriodState.BENCHED.toString())) {
						benched++;
					}
				} else {
					if(period.getEmployee().getEmployeeProfil().getEmployeeCurrentState().equals(EmployeeCurrentState.WORKING_FOR_CLIENT.toString())) {
						workingForClient++;
					}
					if(period.getEmployee().getEmployeeProfil().getEmployeeCurrentState().equals(EmployeeAccountingPeriodState.BENCHED.toString())) {
						benched++;
					}
				}
			}
		}
		status.setCheckedByApproverCount(Integer.toString(checkedByApproverCount));
		status.setCheckedByClientAdminCount(Integer.toString(checkedByClientAdminCount));
		status.setInitialCount(Integer.toString(initialCount));
		status.setRejectedByApproverCount(Integer.toString(rejectedByApproverCount));
		status.setRejectedByClientAdminCount(Integer.toString(rejectedByClientAdminCount));
		status.setSentToApproverCount(Integer.toString(sentToApproverCount));
		status.setSentToClientAdminCount(Integer.toString(sentToClientAdminCount));
		if(userRole.equals("clientAdmin")) {
			status.setNotConfigured(Integer.toString(notConfigured));
			status.setWorkingForClient(Integer.toString(workingForClient));
			status.setBenched(Integer.toString(benched));
		}
		return status;
	}
	
	public static boolean isAllCalendarChecked(List<EmployeeAccountingPeriod> periods) {
		boolean result = true;
		for(EmployeeAccountingPeriod period: periods) {
			if(!(period.getEmployeeAccountingPeriodState().equals(EmployeeAccountingPeriodState.CHECKED_BY_CLIENT_ADMIN.toString()))) {
				return false;
			}
		}
		return result;
	}
	
	public static EmployeeAccountingPeriod getCurrentEmployeeAccPeriod(String role,int userId) {
		EmployeeAccountingPeriod currentAccPeriod = null;
		if(role.equals("employee")) {
			Employee employee = Employee.getEmployeeWithEmployeeAccPeriodsFromDB("id_user", userId);
			List<EmployeeAccountingPeriod> emplPeriods = employee.getEmployeeAccountingPeriods();
			for(EmployeeAccountingPeriod period: emplPeriods) {
				if(period.getClientAccountingPeriod().getClientAccountingPeriodState().equals("open")) {
					return period;
				}
			}
		} else if(role.equals("approver")) {
			Approver approver = Approver.getApproverWithEmployeeAccPeriodsFromDB("id_user", userId);
			List<EmployeeAccountingPeriod> emplPeriods = approver.getApproverAccountingPeriods();
			for(EmployeeAccountingPeriod period: emplPeriods) {
				if(period.getClientAccountingPeriod().getClientAccountingPeriodState().equals("open")) {
					return period;
				}
			}
		} else {
			/* do nothing */
		}
		return currentAccPeriod;
	}
	
	public static List<EmployeeAccountingPeriod> getCurrentEmployeeAccPeriods(List<User> users) {
		List<EmployeeAccountingPeriod> currentAccPeriods = new ArrayList<EmployeeAccountingPeriod>();
		for(User user: users) {
			currentAccPeriods.add(getCurrentEmployeeAccPeriod(user.getRole().getName(),user.getId_user()));
		}
		return currentAccPeriods;
	}
	
	public static String getCurrentEmployeeAccountinPeriodState(String role, int userId) {
		String state = "close";
		EmployeeAccountingPeriod emplPeriod = getCurrentEmployeeAccPeriod(role, userId);
		if(emplPeriod == null) return null;
		if(emplPeriod.getEmployeeAccountingPeriodState() != null) {
			return "open";
		}
		return state;
	}
	
	public static List<EmployeeAccountingPeriod> getEmployeeAccountingPeriodsFromDB(List<User> users) {
		List<EmployeeAccountingPeriod> periods = new ArrayList<EmployeeAccountingPeriod>();
		List<EmployeeAccountingPeriod> tmp = new ArrayList<EmployeeAccountingPeriod>();
		
		for(User user: users) {
			if(user.getRole().getName().equals("employee")) {
				Employee employee = Employee.getEmployeeWithEmployeeAccPeriodsFromDB("id_user", user.getId_user());
				tmp = employee.getEmployeeAccountingPeriods();
			} else {
				Approver approver = Approver.getApproverWithEmployeeAccPeriodsFromDB("id_user", user.getId_user());
				tmp = approver.getApproverAccountingPeriods();
			}
			for(EmployeeAccountingPeriod period: tmp) {
				periods.add(period);
			}
		}
		return periods;
	}
	
	public static List<EmployeeAccountingPeriod> getEmployeeAccountingPeriodsFromDB(List<User> users, String from) {
		List<EmployeeAccountingPeriod> periods = new ArrayList<EmployeeAccountingPeriod>();
		List<EmployeeAccountingPeriod> tmp = new ArrayList<EmployeeAccountingPeriod>();
		
		SimpleDateFormat dateFromater = new SimpleDateFormat("yyyy-MM-dd");
		for(User user: users) {
			if(user.getRole().getName().equals("employee")) {
				Employee employee = Employee.getEmployeeWithEmployeeAccPeriodsFromDB("id_user", user.getId_user());
				tmp = employee.getEmployeeAccountingPeriods();
			} else {
				Approver approver = Approver.getApproverWithEmployeeAccPeriodsFromDB("id_user", user.getId_user());
				tmp = approver.getApproverAccountingPeriods();
			}
			for(EmployeeAccountingPeriod period: tmp) {
				if(period.getClientAccountingPeriod().getClientAccountingPeriodState().equals(ClientAccountingPeriodState.OPEN.toString())) {
					if(dateFromater.format(period.getCalFrom().getTime()).equals(from)) {
						periods.add(period);
					}
				}
			}
		}
		if((periods.size()) == 0) return null;
		return periods;
	}
	
	public static List<EmployeeAccountingPeriod> getEmployeeAccountingPeriodsWithEntriesFromDB(List<EmployeeAccountingPeriod> periods) {
		return DAOFactory.getInstance().getLuteInterface().getEmployeeAccountingPeriodsWithEntriesFromDataBase(periods);
	}
	
	public static List<EmployeeAccountingPeriod> getEmployeeAccountingPeriodsFromDB(List<Integer> employeeIds, GregorianCalendar from) {
		return DAOFactory.getInstance().getLuteInterface().getEmployeeAccountingPeriodsFromDataBase(employeeIds,from);
	}
	
	public static List<EmployeeAccountingPeriod> getEmployeeAccountingPeriodsForSpecifiedClientFromDB(int clientId) {
		return DAOFactory.getInstance().getLuteInterface().getEmployeeAccountingPeriodsByClientIdFromDataBase("clientAccountingPeriod.id", clientId);
	}
	
	public static List<EmployeeAccountingPeriod> getEmployeeAccountingPeriodsForSpecifiedClientFromDB(int id_client, GregorianCalendar from) {
		List<EmployeeAccountingPeriod> employeeAccPeriods = null;
		ClientAccountingPeriod clientPeriod = 
				ClientAccountingPeriod.getClientAccountingPeriodWithEmployeeAccPeriodsBySpecifiedClientFromDB("id_client_accounting_period", id_client,
																											  "date_from", from);
		employeeAccPeriods = clientPeriod.getEmployeeAccountingPeriod();
		if(employeeAccPeriods != null) {
			return employeeAccPeriods;
		}
		return null;
	}
	
	public static EmployeeAccountingPeriod getEmployeeAccountingPeriodByIdDB(String propertyName, int propertyValue) {
		return DAOFactory.getInstance().getLuteInterface().getEmployeeAccountingPeriodFromDataBaseById(propertyName, propertyValue);
	}
	
	public static void updateEmployeeAccountingPeriodInDB(EmployeeAccountingPeriod period) {
		DAOFactory.getInstance().getLuteInterface().updateObjectinDataBase(period);
	}
	
	public static void updateInDB(List<EmployeeAccountingPeriod> periods) throws IllegalStateException {
		DAOFactory.getInstance().getLuteInterface().updateEmployeeAccountingPeriodsInDataBase(periods);
	}
	
	public static void addEmployeeAccountingPeriodToDB(EmployeeAccountingPeriod period) {
		DAOFactory.getInstance().getLuteInterface().addObjectToDataBase(period);
	}

}
